package com.htfyun.factorytest.BluetoothBondManager;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.SparseArray;

import com.htfyun.factorytest.utils.DebugLog;

import java.lang.reflect.Method;

public class BluetoothBondManager {

    private final BluetoothAdapter bluetoothAdapter;

    private final Context mContext;

    private OnBluetoothDiscoveryListener discoveryListener;
    private OnBluetoothEnabledListener enabledListener;
    private OnBluetoothFoundListener foundListener;
    private OnBluetoothBondListener bondListener;
    private OnBluetoothACLConnectedListener aclConnectedListener;

    private SparseArray<BluetoothProfile> bluetoothProfileSparseArray = new SparseArray<>();

    public BluetoothBondManager(Context ctx) {
        this.mContext = ctx;
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    }

    public BluetoothAdapter getBluetoothAdapter() {
        return bluetoothAdapter;
    }

    /**
     * 打开bt
     */
    public void openBluetooth() {
        if (!isBluetoothEnabled() && null != bluetoothAdapter) {
            bluetoothAdapter.enable();
        }
    }

    /**
     * 关闭bt
     */
    public void closeBluetooth() {

        if (isBluetoothEnabled() && null != bluetoothAdapter) {
            bluetoothAdapter.disable();
        }
    }

    public boolean isBluetoothEnabled() {
        return null != bluetoothAdapter && bluetoothAdapter.isEnabled();
    }

    public void startDiscovery() {

        if (isBluetoothEnabled()) {
            bluetoothAdapter.startDiscovery();
        }
    }

    public void cancelDiscovery() {

        if (bluetoothAdapter.isEnabled() && bluetoothAdapter.isDiscovering()) {
            bluetoothAdapter.cancelDiscovery();
        }

    }

    public boolean isDiscovering() {

        return (bluetoothAdapter.isEnabled() && bluetoothAdapter.isDiscovering());

    }

    public void bondDevice(BluetoothDevice device) {
        if (device == null) {
            return;
        }

        disconnectAllProfile();
        bluetoothProfileSparseArray.clear();

        cancelDiscovery();
        int tryCount = 7;
        try {
            while (tryCount-- > 0) {
                if (device.createBond()) {
                    DebugLog.e("createBond ok");
                    break;
                }
                DebugLog.e("createBond fail, removeBond and trycount = " + tryCount);

                removeBond(device);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private boolean createBond(BluetoothDevice btDevice)
            throws Exception {
        Method createBondMethod = BluetoothDevice.class.getMethod("createBond");
        Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
        return returnValue;
    }

    public boolean removeBond(BluetoothDevice btDevice) {

        try {
            Method createBondMethod = BluetoothDevice.class.getMethod("removeBond");
            Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
            return returnValue;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;

    }

    private void tryConnect(final BluetoothDevice device) {

        BluetoothClass bluetoothClass = device.getBluetoothClass();

        boolean match_headset = doesClassMatch(bluetoothClass, PROFILE_HEADSET);
        boolean match_a2dp = doesClassMatch(bluetoothClass, PROFILE_A2DP);

        if (match_headset) {
            connectProfile(device, BluetoothProfile.HEADSET);
        }

        if (match_a2dp) {
            connectProfile(device, BluetoothProfile.A2DP);
        }

    }

    private static final int PROFILE_HEADSET = 0;
    private static final int PROFILE_A2DP = 1;
    private static final int PROFILE_OPP = 2;
    private static final int PROFILE_HID = 3;
    private static final int PROFILE_PANU = 4;
    private static final int PROFILE_NAP = 5;
    private static final int PROFILE_A2DP_SINK = 6;
    private boolean doesClassMatch(BluetoothClass bluetoothClass, int profile) {

        try {

            Method method = bluetoothClass.getClass().getMethod("doesClassMatch", int.class);
            method.setAccessible(true);
            Boolean returnValue = (Boolean) method.invoke(bluetoothClass, profile);
            return returnValue;
        } catch (Exception e) {
            e.printStackTrace();
            DebugLog.e("doesClassMatch err = " + e.getMessage());
            return false;
        }

    }

    public void disconnectAllProfile() {

        if (bluetoothAdapter == null) {
            return;
        }

        for (int i = 0; i < bluetoothProfileSparseArray.size(); i++) {
            int profile = bluetoothProfileSparseArray.keyAt(i);
            BluetoothProfile proxy = bluetoothProfileSparseArray.get(profile);
            bluetoothAdapter.closeProfileProxy(profile, proxy);
        }

        bluetoothProfileSparseArray.clear();

    }

    private void connectProfile(final BluetoothDevice device, final int profile) {


        bluetoothAdapter.getProfileProxy(mContext, new BluetoothProfile.ServiceListener() {
            @Override
            public void onServiceConnected(int profile, BluetoothProfile proxy) {

                bluetoothProfileSparseArray.put(profile, proxy);

                boolean ok = connect(proxy, device);
                if (ok) {
                    if (bondListener != null) {
                        bondListener.onBluetoothConnected(device, profile);
                    }
                }

            }

            @Override
            public void onServiceDisconnected(int profile) {

                bluetoothProfileSparseArray.remove(profile);

                if (bondListener != null) {
                    bondListener.onBluetoothDisconnected(device, profile);
                }
            }
        }, profile);

    }

    private boolean connect(BluetoothProfile profile, BluetoothDevice device) {
        try {

            Method method = profile.getClass().getMethod("connect", BluetoothDevice.class);
            method.setAccessible(true);
            Boolean returnValue = (Boolean) method.invoke(profile, device);
            return returnValue;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    public void registerBTReceiver() {

        mContext.registerReceiver(btReceiver, getIntentFilterOfBT());
    }

    public void unregisterBTReceiver() {
        mContext.unregisterReceiver(btReceiver);
    }


    private IntentFilter getIntentFilterOfBT() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
        intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
        intentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);


        return intentFilter;
    }


    ///////////////////////////////////////////////
    private final BroadcastReceiver btReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (action == null) {
                return;
            }

            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {

                String stateExtra = BluetoothAdapter.EXTRA_STATE;
                int state = intent.getIntExtra(stateExtra, -1);

                switch(state) {
                    case BluetoothAdapter.STATE_TURNING_ON:
                        break;
                    case BluetoothAdapter.STATE_ON:
                        if (null != enabledListener) {
                            enabledListener.onBluetoothEnabled(true);
                        }
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        if (null != enabledListener) {
                            enabledListener.onBluetoothEnabled(false);
                        }
                        break;
                    case BluetoothAdapter.STATE_OFF:
                        break;
                }

            } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {

                DebugLog.e("~~~~~~~~~~~~~~ACTION_ACL_CONNECTED");

                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

                if (aclConnectedListener != null) {
                    aclConnectedListener.onBluetoothACLConnected(device, true);
                }

            } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {

                DebugLog.e("~~~~~~~~~~~~~~ACTION_ACL_DISCONNECTED");

                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

                if (aclConnectedListener != null) {
                    aclConnectedListener.onBluetoothACLConnected(device, false);
                }

            } else if (action.equals(BluetoothDevice.ACTION_FOUND)) {

                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//                short rssi = intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI);
                if (device != null && foundListener != null) {
                    foundListener.onBluetoothFound(device);
                }


            } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {

                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

                if (device != null) {

                    int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);


                    switch (state) {
                        case BluetoothDevice.BOND_NONE:
                            break;
                        case BluetoothDevice.BOND_BONDING:
                            if (bondListener != null) {
                                bondListener.onBluetoothBonding(device);
                            }
                            break;
                        case BluetoothDevice.BOND_BONDED:

                            tryConnect(device);

                            if (bondListener != null) {
                                bondListener.onBluetoothBonded(device);
                            }
                            break;
                    }

                }

            } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED)) {

                if (discoveryListener != null) {
                    discoveryListener.onDiscoveryStarted();
                }

            } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {

                if (discoveryListener != null) {
                    discoveryListener.onDiscoveryFinished();
                }
            }
        }
    };


    ///////////////////////////////////////////////


    public void setDiscoveryListener(OnBluetoothDiscoveryListener discoveryListener) {
        this.discoveryListener = discoveryListener;
    }

    public void setEnabledListener(OnBluetoothEnabledListener enabledListener) {
        this.enabledListener = enabledListener;
    }

    public void setFoundListener(OnBluetoothFoundListener foundListener) {
        this.foundListener = foundListener;
    }

    public void setBondListener(OnBluetoothBondListener bondListener) {
        this.bondListener = bondListener;
    }

    public void setAclConnectedListener(OnBluetoothACLConnectedListener aclConnectedListener) {
        this.aclConnectedListener = aclConnectedListener;
    }
}
